Skip to contentMethod: lambda$renderTrack$15(MapViewExampleController.1PointsAndArea, MapView.OverlayHelper)
1: /*
2: * *************************************************************************************************************************************************************
3: *
4: * MapView: a JavaFX map renderer for tile-based servers
5: * http://tidalwave.it/projects/mapview
6: *
7: * Copyright (C) 2024 - 2025 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *************************************************************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12: * You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17: * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18: *
19: * *************************************************************************************************************************************************************
20: *
21: * git clone https://bitbucket.org/tidalwave/mapview-src
22: * git clone https://github.com/tidalwave-it/mapview-src
23: *
24: * *************************************************************************************************************************************************************
25: */
26: package it.tidalwave.mapview.javafx.example;
27:
28: import jakarta.annotation.Nonnull;
29: import java.util.Collection;
30: import java.util.List;
31: import java.util.concurrent.ExecutionException;
32: import java.util.concurrent.ExecutorService;
33: import java.util.concurrent.Executors;
34: import java.io.IOException;
35: import javafx.concurrent.Task;
36: import javafx.fxml.FXML;
37: import javafx.scene.Node;
38: import javafx.scene.control.Button;
39: import javafx.scene.control.Label;
40: import javafx.scene.control.Slider;
41: import javafx.scene.layout.AnchorPane;
42: import javafx.scene.paint.Color;
43: import javafx.scene.shape.Circle;
44: import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
45: import io.jenetics.jpx.GPX;
46: import io.jenetics.jpx.Track;
47: import io.jenetics.jpx.TrackSegment;
48: import io.jenetics.jpx.WayPoint;
49: import it.tidalwave.mapviewer.MapArea;
50: import it.tidalwave.mapviewer.MapCoordinates;
51: import it.tidalwave.mapviewer.OpenStreetMapTileSource;
52: import it.tidalwave.mapviewer.OpenTopoMapTileSource;
53: import it.tidalwave.mapviewer.TileSource;
54: import it.tidalwave.mapviewer.javafx.MapView;
55: import lombok.extern.slf4j.Slf4j;
56:
57: /***************************************************************************************************************************************************************
58: *
59: * @author Fabrizio Giudici
60: *
61: **************************************************************************************************************************************************************/
62: @Slf4j
63: public class MapViewExampleController
64: {
65: private static final String TRACK_OVERLAY_NAME = "track";
66: private static final MapCoordinates START = MapCoordinates.of(44.4072, 8.9340);
67: private static final double START_ZOOM = 8;
68: private static final MapArea ITALY = MapArea.of(47.115, 18.480, 36.6199, 6.749);
69: private static final MapArea FRANCE = MapArea.of(51.148, 9.560, 2.053, -54.524);
70: private static final MapArea SWITZERLAND = MapArea.of(47.830, 10.442, 45.776, 6.022);
71: private static final MapArea ALEUTIAN = MapArea.of(62.67, -147.38, 46.32, 161.64 );
72: private static final TileSource osm = new OpenStreetMapTileSource();
73: private static final TileSource otm = new OpenTopoMapTileSource();
74:
75: @FXML
76: private AnchorPane apAnchorPane;
77:
78: @FXML
79: private Slider slZoom;
80:
81: @FXML
82: private Button btZoomIn;
83:
84: @FXML
85: private Button btZoomOut;
86:
87: @FXML
88: private Button btReset;
89:
90: @FXML
91: private Button btShowItaly;
92:
93: @FXML
94: private Button btShowFrance;
95:
96: @FXML
97: private Button btShowSwitzerland;
98:
99: @FXML
100: private Button btShowAleutian;
101:
102: @FXML
103: private Button btReframe;
104:
105: @FXML
106: private Button btOSM;
107:
108: @FXML
109: private Button btOTM;
110:
111: @FXML
112: private Label lbCoordinates;
113:
114: @FXML
115: private Label lbArea;
116:
117: @FXML
118: private Label lbCenterCoordinates;
119:
120: @FXML
121: private Label lbZoom;
122:
123: private MapView mapView;
124:
125: @Nonnull
126: private final ExecutorService executorService = Executors.newFixedThreadPool(1);
127:
128: /***********************************************************************************************************************************************************
129: *
130: **********************************************************************************************************************************************************/
131: public void initialize()
132: {
133: mapView = new MapView(MapView.options());
134: mapView.setZoom(START_ZOOM);
135: mapView.setCenter(START);
136: mapView.setRecenterOnDoubleClick(true);
137: AnchorPane.setLeftAnchor(mapView, 0.0);
138: AnchorPane.setRightAnchor(mapView, 0.0);
139: AnchorPane.setTopAnchor(mapView, 0.0);
140: AnchorPane.setBottomAnchor(mapView, 0.0);
141: apAnchorPane.getChildren().add(mapView);
142: mapView.setRecenterOnDoubleClick(true);
143: slZoom.minProperty().bind(mapView.minZoomProperty());
144: slZoom.maxProperty().bind(mapView.maxZoomProperty());
145: slZoom.valueProperty().bindBidirectional(mapView.zoomProperty());
146: mapView.centerProperty().addListener((_1, _2, coordinates) -> lbCenterCoordinates.setText(coordinates.toString()));
147: mapView.zoomProperty().addListener((_1, _2, zoom) -> lbZoom.setText(Integer.toString(zoom.intValue())));
148: mapView.areaProperty().addListener((_1, _2, area) -> lbArea.setText(area.toString()));
149: mapView.coordinatesUnderMouseProperty().addListener((_1, _2, coordinates) -> lbCoordinates.setText(coordinates.toString()));
150: btZoomIn.setOnAction(event -> mapView.setZoom(mapView.getZoom() + 1));
151: btZoomOut.setOnAction(event -> mapView.setZoom(mapView.getZoom() - 1));
152: btReset.setOnAction(event -> { mapView.setCenter(START); mapView.setZoom(START_ZOOM); });
153: btShowItaly.setOnAction(event -> mapView.fitArea(ITALY));
154: btShowFrance.setOnAction(event -> mapView.fitArea(FRANCE));
155: btShowSwitzerland.setOnAction(event -> mapView.fitArea(SWITZERLAND));
156: btShowAleutian.setOnAction(event -> mapView.fitArea(ALEUTIAN));
157: btOSM.setOnAction(event -> mapView.setTileSource(osm));
158: btOTM.setOnAction(event -> mapView.setTileSource(otm));
159: btReframe.setOnAction(event -> renderTrack());
160: }
161:
162: /***********************************************************************************************************************************************************
163: *
164: **********************************************************************************************************************************************************/
165: @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE")
166: public void renderTrack()
167: {
168: record PointsAndArea(List<WayPoint> points, MapArea area) {}
169:
170: final var task = new Task<PointsAndArea>()
171: {
172: @Override @Nonnull
173: protected PointsAndArea call()
174: {
175: final var track = loadTrack();
176: final var points = track.segments().flatMap(TrackSegment::points).toList();
177: final var area = computeFitArea(points);
178: log.info("track with {} points, fit area: {}", points.size(), area);
179: return new PointsAndArea(points, area);
180: }
181: };
182:
183: task.setOnSucceeded(event ->
184: {
185: try
186: {
187: final var pointsAndArea = task.get();
188: mapView.removeOverlay(TRACK_OVERLAY_NAME);
189: mapView.addOverlay(TRACK_OVERLAY_NAME, helper ->
190: helper.addAll(pointsAndArea.points.stream().map(wp -> createPoint(helper, wp)).toList()));
191: mapView.fitArea(pointsAndArea.area);
192: }
193: catch (InterruptedException | ExecutionException e)
194: {
195: log.error("", e);
196: }
197: });
198:
199: executorService.submit(task);
200: }
201:
202: /***********************************************************************************************************************************************************
203: *
204: **********************************************************************************************************************************************************/
205: @Nonnull
206: private static Node createPoint (@Nonnull final MapView.OverlayHelper helper, @Nonnull final WayPoint wp)
207: {
208: final var mapPoint = helper.toMapViewPoint(MapCoordinates.of(wp.getLatitude().doubleValue(), wp.getLongitude().doubleValue()));
209: final var node = new Circle(2.5, Color.RED);
210: node.setVisible(true);
211: node.setTranslateX(mapPoint.x());
212: node.setTranslateY(mapPoint.y());
213: return node;
214: }
215:
216: /***********************************************************************************************************************************************************
217: *
218: **********************************************************************************************************************************************************/
219: @Nonnull
220: private static MapArea computeFitArea (@Nonnull final Collection<WayPoint> points)
221: {
222: // quick and dirty, just for this example
223: var north = -999d;
224: var south = 999d;
225: var east = -999d;
226: var west = 999d;
227:
228: for (final var point : points)
229: {
230: north = Math.max(north, point.getLatitude().doubleValue());
231: south = Math.min(south, point.getLatitude().doubleValue());
232: east = Math.max(east, point.getLongitude().doubleValue());
233: west = Math.min(west, point.getLongitude().doubleValue());
234: }
235:
236: return MapArea.of(north, east, south, west);
237: }
238:
239: /***********************************************************************************************************************************************************
240: *
241: **********************************************************************************************************************************************************/
242: @Nonnull
243: private Track loadTrack()
244: {
245: try (final var is = MapViewExampleController.class.getResourceAsStream("/2014-04-10 1800__20140410_1800.gpx"))
246: {
247: return GPX.Reader.of(GPX.Reader.Mode.LENIENT).read(is).getTracks().getFirst();
248: }
249: catch (IOException e)
250: {
251: log.error(e.toString());
252: throw new RuntimeException(e);
253: }
254: }
255: }